package com.share.device.emqx.handler.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.share.common.core.utils.StringUtils;
import com.share.common.rabbit.constant.MqConst;
import com.share.common.rabbit.service.RabbitService;
import com.share.device.domain.*;
import com.share.device.emqx.annotation.YEmqx;
import com.share.device.emqx.constants.EmqxConstants;
import com.share.device.emqx.handler.MessageHandler;
import com.share.device.service.ICabinetService;
import com.share.device.service.ICabinetSlotService;
import com.share.device.service.IPowerBankService;
import com.share.device.service.IStationService;
import com.share.order.domain.SubmitOrderVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 用户使用充电宝实现类 弹出充电宝
 * emqx消息格式:
 * {"messageNo":"7777","slotNo":"1","userId":1,
 * "cabinetNo":"xgxgxxxg","powerBankNo":"gg001"}
 */
@Slf4j
@Component
@YEmqx(topic = EmqxConstants.TOPIC_POWERBANK_UNLOCK)
public class PowerBankPopHandler implements MessageHandler {

    @Autowired
    private ICabinetService cabinetService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private IPowerBankService powerBankService;
    @Autowired
    private ICabinetSlotService cabinetSlotService;
    @Autowired
    private IStationService stationService;
    @Autowired
    private RabbitService rabbitService;

    /**
     * 更新充电宝 柜机 插槽相关属性
     * 生成订单
     * @param message
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void handleMessage(JSONObject message) {
        log.info("用户扫码 弹出充电宝: {}", message.toJSONString());

        //测试用延时
//        try {
//            Thread.sleep(5000);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }

        //1.获取消息编号messageNo，这是消息的唯一标识，并且防止柜机消息重复提交到emqx，生成重复订单
        String messageNo = message.getString("mNo");

        //基于Redis分布式锁 实现对消息上锁，防止对消息重复处理
        //约定key的格式，可唯一标识一个消息
        String key = "powerBank:unlock:"+messageNo;

        //setIfAbsent = setnx 只有当key不存在时才创建，key存在不会执行任何操作，返回0（false）  过期时间为1h
        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, messageNo, 1, TimeUnit.HOURS);

        if (ifAbsent == false){ // 说明已经存在的消息，不需要完成相关业务操作，直接return
            log.info("重复请求: {}", message.toJSONString());
            return;
        }

        //2.获取消息中的数据，slotNo  userId  cabinetNo  powerBankNo
        Long userId = message.getLong("uId"); //用户id
        String cabinetNo = message.getString("cNo"); //柜机编号
        String powerBankNo = message.getString("pNo");//充电宝编号
        String slotNo = message.getString("sNo");//插槽编号

        //判断数据是否为空 有为空的数据则直接报错并返回
        if (StringUtils.isEmpty(cabinetNo)
                || StringUtils.isEmpty(powerBankNo)
                || StringUtils.isEmpty(slotNo)
                || userId == null ) {
            log.info("必要数据为空: {}", message.toJSONString());
            return;
        }

        //3.根据柜机编号获取柜机信息
        Cabinet cabinet = cabinetService.getByCabinetNo(cabinetNo);

        //4.根据充电宝编号获取充电宝信息
        PowerBank powerBank = powerBankService.getByPowerBankNo(powerBankNo);

        //5.根据柜机id和插槽编号获得slot信息
        CabinetSlot cabinetSlot = cabinetSlotService.getBtSlotNo(cabinet.getId(), slotNo);

        //6.根据柜机id获取站点数据
        LambdaQueryWrapper<Station> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Station::getCabinetId,cabinet.getId());
        Station station = stationService.getOne(wrapper);

        //7.更新充电宝 插槽 柜机的状态 写入数据库
        //更新充电宝 状态（0:未投放 1：可用 2：已租用 3：充电中 4：故障）
        powerBank.setStatus("2"); //将充电宝状态改为2
        powerBankService.updateById(powerBank);

        //更新对应插槽状态  状态（1：占用 0：空闲）
        cabinetSlot.setStatus("0"); //充电宝弹出后改插槽为空闲
        cabinetSlot.setPowerBank(null);
        cabinetSlot.setUpdateTime(new Date());
        cabinetSlotService.updateById(cabinetSlot);

        //更新柜机信息
        int freeSlots = cabinet.getFreeSlots() + 1;
        cabinet.setFreeSlots(freeSlots); //可用插槽数+1
        int usedSlots = cabinet.getUsedSlots() - 1;
        cabinet.setUsedSlots(usedSlots); //已使用插槽数-1

        int availableNum = cabinet.getAvailableNum() - 1;
        cabinet.setAvailableNum(availableNum); //可用充电宝数量-1

        cabinet.setUpdateTime(new Date());

        cabinetService.updateById(cabinet);

        // 8.使用RabbitMQ异步创建订单

        //封装消息数据对象 设置需要的数据
        SubmitOrderVo submitOrderVo = new SubmitOrderVo();

        submitOrderVo.setMessageNo(messageNo);//消息id
        submitOrderVo.setUserId(userId);//当前登录用户id
        submitOrderVo.setPowerBankNo(powerBankNo);//弹出充电宝编号
        submitOrderVo.setStartStationId(station.getId());//站点id
        submitOrderVo.setStartStationName(station.getName());//站点名称
        submitOrderVo.setStartCabinetNo(cabinetNo);//柜机编号
        submitOrderVo.setFeeRuleId(station.getFeeRuleId());//计费规则id

        log.info("构建订单对象: {}", JSONObject.toJSONString(submitOrderVo));

        //发送消息
        rabbitService.sendMessage(MqConst.EXCHANGE_ORDER,MqConst.ROUTING_SUBMIT_ORDER,JSONObject.toJSONString(submitOrderVo));

    }
}
